#ifndef DISTANCEQUEUE_HPP_
#define DISTANCEQUEUE_HPP_

#include <iostream>
#include <queue>

#include "../aerDefs.hpp"
#include "../view/aerView3D.hpp"


namespace aer {

enum Order
{
  FRONT_TO_BACK,
  BACK_TO_FRONT
};

///
/// Priority queue used to sort data along an arbitrary axis
///
template<typename T>
class DistanceQueue
{  
  private:
    typedef std::pair<T, float> ProjDist_t;
    
    struct Sorter
    {
      bool bBackToFront;
      Sorter(Order order) : bBackToFront(order==BACK_TO_FRONT) {}
      
      bool operator() (const ProjDist_t &A, const ProjDist_t &B)
      { 
        if (bBackToFront) {
          return A.second < B.second;
        }
        return A.second > B.second;
      }
    };
    
    typedef std::priority_queue<ProjDist_t, std::vector<ProjDist_t>, Sorter> Type;
  
  
  private:
    Type m_queue;
    const View3D *m_pView3D;
  
  
  public:
    explicit
    DistanceQueue(Order order, const View3D *pView3D=NULL)
      : m_queue(Sorter(order)),
        m_pView3D(pView3D)
    {}
    
    void setView3D(const View3D *pView3D) {
      m_pView3D = pView3D;
    }
    
    bool empty() const { return m_queue.empty(); }
    
    size_t size() const { return m_queue.size(); }
    
    
    void push(T elem, const Vector3 &elemPosition)
    {
      AER_ASSERT( m_pView3D != NULL );
      
      aer::Vector3 v = elemPosition - m_pView3D->getPosition();
      float dp = glm::dot( v, m_pView3D->getDirection());
      
      m_queue.push( ProjDist_t( elem, dp) );
    }
    
    
    T pop(float *dp=NULL)
    {
      ProjDist_t top = m_queue.top();
      m_queue.pop();
      if (dp) *dp = top.second;
      return top.first;
    }
};

} // aer

#endif //DISTANCEQUEUE_HPP_
